/*
Copyright 2021 Vladimir Smagin.
*/

package controllers

import (
	"context"
	"fmt"

	"git.blindage.org/21h/single-redis-operator/controllers/manifests"
	raven "github.com/getsentry/raven-go"
	"github.com/go-logr/logr"
	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/runtime"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/reconcile"

	sredisv1 "git.blindage.org/21h/single-redis-operator/api/v1"
)

// SingleRedisReconciler reconciles a SingleRedis object
type SingleRedisReconciler struct {
	client.Client
	Log    logr.Logger
	Scheme *runtime.Scheme
}

// +kubebuilder:rbac:groups=blindage.org,resources=singleredis,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=blindage.org,resources=singleredis/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=blindage.org,resources=singleredis/finalizers,verbs=update

// Reconcile ...
func (r *SingleRedisReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	_ = r.Log.WithValues("singleredis", req.NamespacedName)

	instance := &sredisv1.SingleRedis{}
	err := r.Client.Get(context.TODO(), req.NamespacedName, instance)
	if err != nil {
		if errors.IsNotFound(err) {
			return reconcile.Result{}, nil
		}
		// Error reading the object - requeue the request.
		return reconcile.Result{}, err
	}

	_, err = r.reconcileFinalizers(r.Log, instance)
	if err != nil {
		raven.CaptureErrorAndWait(err, nil)
		return reconcile.Result{}, err
	}

	// Default path is /data, but can be overrided in custom resource
	volumeMountPath := "/data"
	if instance.Spec.PersistentVolumePath != "" {
		volumeMountPath = instance.Spec.PersistentVolumePath
	}

	configmapTemplate := `dir %s
tcp-keepalive 60
save 900 1
save 300 10
`
	// make default config for redis
	if instance.Spec.Configmap == "" {
		configRedisName := instance.Name
		configRedisData := map[string]string{"redis.conf": fmt.Sprintf(configmapTemplate, volumeMountPath)}

		if _, err := r.ReconcileConfigmap(r.Log, instance, configRedisName, configRedisData); err != nil {
			return reconcile.Result{}, err
		}
	}

	// reconcile Redis StatefulSet
	newRedisStatefulset := manifests.GenerateStatefulSet(instance)
	if _, err := r.ReconcileStatefulSet(r.Log, instance, newRedisStatefulset); err != nil {
		return reconcile.Result{}, err
	}

	serviceName := instance.Name
	servicePortName := "redis"
	servicePort := int32(6379)
	serviceSelector := map[string]string{"component": "redis"}
	if _, err := r.ReconcileService(r.Log, instance, serviceName, map[string]int32{servicePortName: servicePort}, serviceSelector); err != nil {
		return reconcile.Result{}, err
	}

	return ctrl.Result{}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *SingleRedisReconciler) SetupWithManager(mgr ctrl.Manager) error {
	return ctrl.NewControllerManagedBy(mgr).
		For(&sredisv1.SingleRedis{}).
		Complete(r)
}
